package com.tomato.spring.framework.injection;

import com.tomato.spring.framework.core.BeanContainer;
import com.tomato.spring.framework.injection.annotation.Autowired;
import com.tomato.spring.framework.util.ClassUtil;
import com.tomato.spring.framework.util.CollectionUtil;
import com.tomato.spring.framework.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.Set;

/**
 * <pre>
 *     DI 整体思路实现如下：
 *     1.获取bean容器中所有的class对象
 *     2.获取class对象中的所有属性
 *     3.判断属性有没有被Autowired注解修饰
 *     4.如果有被修饰则把容器里面的对象注入进去
 *
 * </pre>
 *
 * @author tomato
 */
@Slf4j
public class DependencyInjector {
    private final BeanContainer beanContainer;

    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    public void doIOC() {
        Set<Class<?>> classes =
                beanContainer.getClasses();

        if (CollectionUtil.isEmpty(classes)) {
            log.info("容器中无任何实例，无法注入");
            return;
        }

        for (Class<?> clazz : classes) {
            Field[] fields = clazz.getDeclaredFields();
            if (CollectionUtil.isEmpty(fields)) {
                continue;
            }
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired annotation = field.getAnnotation(Autowired.class);
                    setField(clazz, field, annotation.value());
                }
            }
        }

    }

    public void setField(Class<?> target, Field field, String autowiredValue) {
        Class<?> fieldClass = field.getType();
        //
        Object fieldValue = getImplementFieldInstance(fieldClass, autowiredValue);
        if (fieldValue == null) {
            log.error("不能从容器中 目标类：{} 的属性 {} 实例", target.getName(), field.getName());
            throw new RuntimeException(MessageFormat.format("不能从容器中 目标类：{0} 的属性 {1} 实例", target.getName(), field.getName()));
        }
        ClassUtil.setFiled(field, beanContainer.getBean(target), fieldValue, true);
    }


    public Object getImplementFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object value = beanContainer.getBean(fieldClass);
        if (value != null) {
            return value;
        }

        // 可能存在一个接口多i个实现类情况
        Set<Class<?>> set = beanContainer.getBeanByInterface(fieldClass);
        if (!CollectionUtil.isEmpty(set)) {
            if (StringUtil.isEmpty(autowiredValue)) {
                // 代表接口实现类只有一个，则直接返回
                if (set.size() == 1) {
                    return beanContainer.getBeanByInterface(set.iterator().next());
                }
                log.error(MessageFormat.format("该类型的{0}实例只能注入一个，但是在容器中发现了2个,请在Autowired注解中写上要注入的实例名", fieldClass.getName()));
                throw new RuntimeException(MessageFormat.format("该类型的{0}实例只能注入一个，但是在容器中发现了2个", fieldClass.getName()));
            }
            for (Class<?> aClass : set) {
                if (autowiredValue.equals(aClass.getSimpleName())) {
                    return beanContainer.getBean(aClass);
                }
            }

        }
        return null;
    }


}
